﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Renci.iRods.Common;
using System.Reflection;
using Renci.iRods.Exceptions;

namespace Renci.iRods.Data
{
    public abstract class DataItem
    {
        public bool Exists { get; protected set; }

        protected IRodsConnection Connection { get; private set; }

        public DataItem(IRodsConnection connection)
        {
            this.Connection = connection;
        }

        /// <summary>
        /// Refreshes the state of the object.
        /// </summary>
        public virtual void Refresh()
        {
            throw new NotImplementedException();
        }

        private static Dictionary<Type, Dictionary<DataObjectAttributes, PropertyInfo>> _typeProperties = new Dictionary<Type, Dictionary<DataObjectAttributes, PropertyInfo>>();

        /// <summary>
        /// Loads the item data using provided condition.
        /// </summary>
        /// <param name="addConditions">The add conditions.</param>
        /// <returns></returns>
        protected bool Load(Action<IRodsQuery> addConditions)
        {
            var query = new IRodsQuery();

            Dictionary<DataObjectAttributes, PropertyInfo> typeProperties = null;

            if (!_typeProperties.ContainsKey(this.GetType()))
            {
                lock (_typeProperties)
                {
                    if (!_typeProperties.ContainsKey(this.GetType()))
                    {
                        typeProperties = new Dictionary<DataObjectAttributes, PropertyInfo>();
                        var properties = this.GetType().GetProperties();
                        foreach (var property in properties)
                        {
                            var p = property.GetCustomAttributes(typeof(iRodsColumnAttribute), true).FirstOrDefault() as iRodsColumnAttribute;
                            if (p != null)
                            {
                                typeProperties.Add(p.DataObjectAttribute, property);
                            }
                        }

                        _typeProperties.Add(this.GetType(), typeProperties);
                    }
                    else
                    {
                        typeProperties = _typeProperties[this.GetType()];
                    }
                }
            }
            else
            {
                typeProperties = _typeProperties[this.GetType()];
            }

            foreach (var item in typeProperties.Keys)
            {
                query.AddOutputColumn(item);
            }


            addConditions(query);

            var result = this.Connection.ExecuteQuery(query);

            if (result.Read())
            {
                foreach (var f in typeProperties.Keys)
                {
                    if (typeProperties[f].PropertyType == typeof(string))
                    {
                        typeProperties[f].SetValue(this, result.GetString(f), null);
                    }
                    else if (typeProperties[f].PropertyType == typeof(long))
                    {
                        typeProperties[f].SetValue(this, result.GetInt64(f), null);
                    }
                    else if (typeProperties[f].PropertyType == typeof(int))
                    {
                        typeProperties[f].SetValue(this, result.GetInt32(f), null);
                    }
                    else if (typeProperties[f].PropertyType == typeof(DateTime))
                    {
                        typeProperties[f].SetValue(this, result.GetDateTime(f), null);
                    }
                    else
                    {
                        throw new NotImplementedException();
                    }
                }

                if (result.Read())
                {
                    throw new IRodsException("Multiple entities are found for refresh.");
                }

                return true;
            }
            else
            {
                return false;
            }
        }
    }
}
